using System;
using System.Collections;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using System.Management;
using Biolink.Common.Remoting.Extensions;
using Biolink.Common.Remoting.Sinks;

namespace Biolink.Common.Remoting
{
    using Biolink.Security;
    using Biolink.Common;
    using Biolink.Remoting.Channels.Tcp;

    /// <summary>
    /// Summary description for ICTServerChannel.
    /// </summary>
    public interface ICTServerChannel: ICTRemotingChannel
    {
        ArrayList IPs { get;set;}
        int Port {get;set;}
        int StartUpTimeOut {get;set;}
    }

    /// <summary>
    /// Summary description for CTServerRemoting.
    /// </summary>
    public class CTServerRemotingChannel: CTRemotingChannel, ICTServerChannel
    {
        #region CTServerChannelContext Class
        class CTServerChannelContext: CTRemotingChannelContext
        {
            public void Activate(bool activate)
            {
                if (activate && null == _channel)
                {
                    try
                    {

                        TrafficDefaultInfo.Bind();

                        string msg0 = "Activate server-channel [" + IP + ":" + Port.ToString() + "]. Publication uri of '" + Path.GetFileName(Assembly.GetEntryAssembly().Location) +  "' are:";
                        CTTracer.WriteLine(this, msg0);
                        Console.WriteLine(msg0);
                        Trace.WriteLine(msg0);

                        IServerChannelSinkProvider customServerProvider = null;
                        IClientChannelSinkProvider customClientProvider = null;

                        Hashtable trafficProviderParameters = new Hashtable();
                        trafficProviderParameters["customSinkType"] = typeof(TrafficSink).AssemblyQualifiedName;
                        CustomServerSinkProvider trafficProvider = new CustomServerSinkProvider(trafficProviderParameters, new ArrayList());
                        
                        IDictionary ChannelProperties = new Hashtable();
                        ChannelProperties["name"] = this.ChannelName + "_" + IP;
                        ChannelProperties["port"] = Port;
                        if (IP != null && IP.Length > 0)
                            ChannelProperties["bindTo"] = IP;

                        ChannelProperties["exclusiveAddressUse"] = false; 

                        IDictionary serverFormaterProperties = new Hashtable();
                        serverFormaterProperties["includeVersions"] = false;
                        BinaryServerFormatterSinkProvider serverProviderBinaryFormatter = new BinaryServerFormatterSinkProvider(serverFormaterProperties, null);            
                        serverProviderBinaryFormatter.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;


                        trafficProvider.Next = serverProviderBinaryFormatter;
                        customServerProvider = trafficProvider;


                        // authorization sink
                        if(AuthorizationType == CTRemotingAuthorizationType.SBAPV1)
                        {
                            IServerChannelSinkProvider authrProvider = null;
                            CTTracer.WriteLine(this, "Authorization switched on");
                            if(AuthorizationCenter == null)
                            {
                                CTTracer.WriteLine(this, "Exception: There's no valid Authorization Center for SBAPV1 protocol."  );
                                throw new ArgumentException();
                            }
                            IDictionary authProviderProps = new Hashtable();
                            authProviderProps["writeToConsole"] = true;
                            authrProvider = new SBAPServerChannelSinkChain(authProviderProps, null, customServerProvider, AuthorizationCenter);
                            // set top sink
                            customServerProvider = authrProvider;
                        }
                        else
                            CTTracer.WriteLine(this, "Authorization switched off");

                        // crypto sink
                        if(CryptoType == CTRemotingCryptoType.SBCPV1)
                        {
                            CTTracer.WriteLine(this, "Crypto switched on");
                            IServerChannelSinkProvider cryptoProvider = null;
                            IDictionary cryptoProviderProps = new Hashtable();
                            cryptoProviderProps["writeToConsole"] = true;
                            cryptoProvider = new SBCPServerChannelSinkChain(cryptoProviderProps, null, customServerProvider);
                            // update top sink
                            customServerProvider = cryptoProvider;
                        }
                        else
                            CTTracer.WriteLine(this, "Crypto switched off");

                        
                        

                        IDictionary clientFormaterProperties = new Hashtable();
                        clientFormaterProperties["includeVersions"] = false;
                        BinaryClientFormatterSinkProvider clientProviderBinaryFormatter = new BinaryClientFormatterSinkProvider(clientFormaterProperties, null);

                        customClientProvider = clientProviderBinaryFormatter;

                        // authorization sink
                        if(AuthorizationType == CTRemotingAuthorizationType.SBAPV1)
                        {
                            CTTracer.WriteLine(this, "Authorization switched on");
                            //Console.WriteLine("Authorization switched on");
                            IDictionary authProviderProps = new Hashtable();
                            authProviderProps["writeToConsole"] = false;
                            IClientChannelSinkProvider clientAuthrProvider = new SBAPClientChannelSinkChain(authProviderProps, null, customClientProvider);
                            // set top sink
                            customClientProvider = clientAuthrProvider;
                        }

                        // crypto sink
                        if(CryptoType == CTRemotingCryptoType.SBCPV1)
                        {
                            CTTracer.WriteLine(this, "Crypto switched on");
                            //Console.WriteLine("Crypto switched on");
                            IClientChannelSinkProvider cryptoProvider = null;
                            IDictionary cryptoProviderProps = new Hashtable();
                            cryptoProviderProps["writeToConsole"] = false;
                            cryptoProvider = new SBCPClientChannelSinkChain(cryptoProviderProps, null, customClientProvider);
                            // update top sink
                            customClientProvider = cryptoProvider;
                        }


                        // create channel
                        _channel = new TcpChannel(ChannelProperties, customClientProvider, customServerProvider);
                        //_Channel = new TcpChannel(ChannelProperties, null, new SoapServerFormatterSinkProvider());

                        ChannelServices.RegisterChannel(_channel);

                        ChannelDataStore data = (ChannelDataStore) _channel.ChannelData;
                        foreach (string uri in data.ChannelUris)
                        {
                            string msg = string.Format("  {0}", uri);
                            Trace.WriteLine(msg);
                            Console.WriteLine(msg);
                        }



                    }
                    catch(System.Exception startupException)
                    {
                        CTTracer.WriteLine(this, "Exception:" + startupException);
                        Trace.WriteLine("Failed to bind Service host. See Details Below" + Environment.NewLine + startupException);
                        throw;
                    }
                }

                else if (!activate && null != _channel)
                {
                    try
                    {
                        CTTracer.WriteLine(this, "Deactivate server-channel [" + IP + ":" + Port.ToString() + "]");
                        ChannelServices.UnregisterChannel(_channel);
                        if (null != _channel) 
                            _channel = null;
                        ChannelName = string.Empty;
                    }
                    catch(System.Exception shutdownException)
                    {
                        CTTracer.WriteLine(this, "Exception:" + shutdownException);
                    }
                }
            }
        }
        #endregion

        protected ArrayList _contexts = new ArrayList();
        protected ArrayList _ips = new ArrayList();
        protected int _port = 0;
        protected bool _activated = false;
        int _iStartUpTimeOut = 0;

	    public CTServerRemotingChannel()
	    {
	    }

        public ArrayList IPs 
        {
            get { return _ips; }
            set
            {
                if (!GetActive())
                {
                    _ips = value; 
                }
                else
                    throw new NotSupportedException();      
            }
        }

        public int Port
        {
            get { return _port; }
            set {_port = value; }
        }

        public int StartUpTimeOut
        {
            get { return _iStartUpTimeOut; }
            set {_iStartUpTimeOut = value; }
        }


        protected override bool GetActive()
        {
            return _activated;
        }

        protected override void SetActive(bool active)
        {
            if (active && !_activated)
            {
                try
                {
                    if (_ips == null || _ips.Count == 0)
                        throw new ApplicationException("Wrong ip address settings");

                    ArrayList ips = _ips;

                    /*if (_ips.Length == 1 && _ips[0].Equals("127.0.0.1"))
                    {
                        ips = GetLocalIP(this.StartUpTimeOut);
                    }*/

                    foreach (string ip in ips)
                    {
                        CTServerChannelContext ctxt = new CTServerChannelContext();
                        ctxt.AuthorizationCenter = this.AuthorizationCenter;
                        ctxt.AuthorizationType = this.AuthorizationType;
                        ctxt.CryptoType = this.CryptoType;
                        ctxt.ChannelName = this.ChannelName;
                        ctxt.IP = ip;
                        ctxt.Port = _port;
                        ctxt.Activate(true);
                        _contexts.Add(ctxt);
                    }
                    _activated = true;
                }
                catch (System.Exception ex)
                {
                    SetActive(false);        
                    throw ex;
                }
            }
            else if(!active && _activated)
            {
                try
                {
                    if(_contexts != null && _contexts.Count > 0)
                    foreach(CTServerChannelContext ctxt in _contexts)
                        ctxt.Activate(false);
                }
                finally
                {
                    _contexts.Clear();
                    _activated = false;
                }
            }
            //else
              //  throw new ApplicationException(String.Format("An attempt to {0} server channel when it has been already {1} ", (active ? "activate" : "deactivate"), (active ? "activated" : "deactivated")));
        }

        public string[] GetLocalIP()
        {
            return GetLocalIP();
        }

        public string[] GetLocalIP(long iTimeout/*in secs*/)
        {
            //Console.WriteLine("iTimeout = {0}s", iTimeout);
            //Select only IP-enabled network Adapters
            long lTimeOut = iTimeout * 1000 * 10000; //tiks
            long lStartTime = DateTime.Now.Ticks;

            while (true)
            {
                try
                {
                    ArrayList machineIPs = new ArrayList();
                    ManagementObjectSearcher query = new ManagementObjectSearcher("SELECT * FROM Win32_NetworkAdapterConfiguration WHERE IpEnabled='True'") ;
                    ManagementObjectCollection queryCollection = query.Get();
                    ManagementObjectCollection.ManagementObjectEnumerator ipEnumerator = 
                        queryCollection.GetEnumerator();
                    //Go to the first item in collection
    				
                    while (ipEnumerator.MoveNext())
                    {
                        ManagementObject moIP = (ManagementObject)ipEnumerator.Current;
                        string[] addresses = (string[])moIP["IPAddress"];
                        foreach (string s2 in addresses)
                        {
                            if (s2 != null)
                            {
                                string address = s2.Trim();
                                //Console.WriteLine("IPAddress = {0}", s2);
                                if (address.Length > 0 && address != "0.0.0.0" && !machineIPs.Contains(address))
                                {
                                    machineIPs.Add(address);
                                }
                            }
                        }			
                    }

                    string[] saIps = new string[machineIPs.Count];
                    for (int iPos = 0; iPos < machineIPs.Count; iPos++)
                        saIps[iPos] = (string)machineIPs[iPos];

                    return saIps;

                }
                catch (System.Exception ex)
                {
                    string sMsg = "Error getting local IP-addresses." + ex.ToString();
                    CTTracer.WriteLine(this, sMsg);

                    long lExecutionTime = DateTime.Now.Ticks - lStartTime;
                    if (lTimeOut - lExecutionTime > 0) // should white and try again
                    {
                        System.Threading.Thread.Sleep(30000); //30secs
                    }
                    else
                    {
                        throw new ApplicationException(sMsg);
                    }
                }
            }
        }


    }
}
